/*
 * @Description:
 * @Autor: dongjunhua
 * @Date: 2020-12-21 09:37:43
 * @LastEditors: dongjunhua
 * @LastEditTime: 2021-08-21 09:34:55
 */

import CryptoJS from 'crypto-js';
import { recognizeId, storageEncrypt } from '@/config';

/**
 * storage util
 * @class Storage
 */
class Storage {
  /**
   * @internal
   * @method _getFullKey
   * @for Storage
   * @param {string} key
   * @return {*}
   */
  static _getFullKey(key) {
    if (typeof key !== 'string' || !key) {
      return false;
    }
    return `${this.recognizeId}:${key}`;
  }

  /**
   * @todo
   * @internal
   * @method _encrypt
   * @for Storage
   * @param {string} value
   * @return {string}
   */
  static _encrypt(value) {
    if (!this.encrypt) {
      return value;
    }
    const encryptedValue = CryptoJS.AES.encrypt(value, recognizeId).toString();
    return encryptedValue;
  }

  /**
   * @internal
   * @method _decrypt
   * @for Storage
   * @param {string} value
   * @return {string}
   */
  static _decrypt(encryptedValue) {
    if (!this.encrypt) {
      return encryptedValue;
    }
    const value = CryptoJS.AES.decrypt(encryptedValue, recognizeId).toString(
      CryptoJS.enc.Utf8
    );
    return value;
  }

  /**
   * @internal
   * @method _setStorage
   * @for Storage
   * @param {string} type
   * @param {string} key
   * @param {*} value
   * @param {number} expired
   */
  static _setStorage(type, key, value, expired = 0) {
    if (!['localStorage', 'sessionStorage'].includes(type)) {
      return;
    }
    const fullKey = this._getFullKey(key);
    if (!fullKey) {
      return;
    }
    if (!value && value !== 0 && value !== false) {
      return;
    }
    const valueObj = {
      value
    };
    if (expired) {
      valueObj.expiredTime = Date.now() + expired;
    } else {
      const exists = window[type].getItem(fullKey);
      if (exists) {
        const existsObj = JSON.parse(this._decrypt(exists));
        const { expiredTime } = existsObj;
        expiredTime && (valueObj.expiredTime = expiredTime);
      }
    }
    const storageValue = this._encrypt(JSON.stringify(valueObj));
    window[type].setItem(fullKey, storageValue);
  }

  /**
   * @method _getStorage
   * @for Storage
   * @param {string} type
   * @param {string} key
   * @return {*}
   */
  static _getStorage(type, key) {
    if (!['localStorage', 'sessionStorage'].includes(type)) {
      return;
    }
    const fullKey = this._getFullKey(key);
    if (!fullKey) {
      return null;
    }
    const exists = window[type].getItem(fullKey);
    if (!exists) {
      return null;
    }
    const existsObj = JSON.parse(this._decrypt(exists));
    const { value, expiredTime } = existsObj;
    if (expiredTime && expiredTime < Date.now()) {
      this.delLocal(key);
      return null;
    }
    return value;
  }

  /**
   * @method _delStorage
   * @for Storage
   * @param {string} type
   * @param {string} key
   */
  static _delStorage(type, key) {
    if (!['localStorage', 'sessionStorage'].includes(type)) {
      return;
    }
    const fullKey = this._getFullKey(key);
    if (fullKey) {
      window[type].removeItem(fullKey);
    } else {
      window[type].clear();
    }
  }

  /**
   * @method addListener
   * @for Storage
   * @param {function} callback
   */
  static addListener(callback) {
    if (typeof callback === 'function') {
      window.addEventListener('storage', callback);
    }
  }

  /**
   * @method removeListener
   * @for Storage
   * @param {function} callback
   */
  static removeListener(callback) {
    if (typeof callback === 'function') {
      window.removeEventListener('storage', callback);
    }
  }

  /**
   * @method setLocal
   * @for Storage
   * @param {string} key
   * @param {*} value
   * @param {number} expired
   */
  static setLocal(key, value, expired = 0) {
    this._setStorage('localStorage', key, value, expired);
  }

  /**
   * @method getLocal
   * @for Storage
   * @param {string} key
   * @return {*}
   */
  static getLocal(key) {
    return this._getStorage('localStorage', key);
  }

  /**
   * @method delLocal
   * @for Storage
   * @param {string} key
   */
  static delLocal(key) {
    this._delStorage('localStorage', key);
  }

  /**
   * @method setSession
   * @for Storage
   * @param {string} key
   * @param {*} value
   * @param {number} expired
   */
  static setSession(key, value, expired = 0) {
    this._setStorage('sessionStorage', key, value, expired);
  }

  /**
   * @method getSession
   * @for Storage
   * @param {string} key
   * @return {*}
   */
  static getSession(key) {
    return this._getStorage('sessionStorage', key);
  }

  /**
   * @method delSession
   * @for Storage
   * @param {string} key
   */
  static delSession(key) {
    this._delStorage('sessionStorage', key);
  }
}

Storage.recognizeId = recognizeId || 'default';
Storage.encrypt = storageEncrypt && true;

export default Storage;
